import type { Ref } from 'vue'
import { ref, computed } from 'vue'
import { currentLanguage, languages } from '@/shared'
import CryptoJS from 'crypto-js'
import { ElMessage } from 'element-plus'
import { useUserMedia } from '@vueuse/core'

// 语音识别相关
export const useRecord = () => {
  // 录音器实例
  let recorder: any

  // 语音识别的 websocket 实例
  let recordWS: WebSocket

  // 当前语音识别 websocket 连接状态
  const recordStatus: Ref<keyof typeof recordStatusMap> = ref('CLOSED')

  // 语音识别可能的状态
  const recordStatusMap = {
    CLOSED: '录音结束',
    CONNECTING: '建立连接中',
    OPEN: '录音中'
  }

  // 从按下按住说话按钮开始弹窗左上角显示的文字提示
  const recordText = computed(() => {
    if (recordStatus.value === 'OPEN') {
      return `${recordStatusMap[recordStatus.value]}（${countdown.value}s）`
    } else {
      return recordStatusMap[recordStatus.value]
    }
  })

  // 识别结果弹窗是否可见
  const recordDialog = ref(false)

  // 识别结果的文字
  const recordMessage = ref('')

  // 录音状态弹窗是否可见
  const recordingDialogVisible = ref(false)

  // 录音中的倒计时
  const countdown = ref(0)

  // 用于清空录音中的倒计时
  let st: number

  // 用于记录从按下按住按钮开始到松开按钮的间隔，决定是否提示说话时间太短
  const recordInterval = ref(0)

  // 初始化录音器
  // https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E8%B0%83%E7%94%A8%E7%A4%BA%E4%BE%8B
  function initRecorder() {
    const RecorderManager = (window as any).RecorderManager
    recorder = new RecorderManager('/record')
    recorder.onStart = () => {
      recordingDialogVisible.value = true
      connectRecordWebSocket()
    }
    recorder.onFrameRecorded = ({
      isLastFrame,
      frameBuffer
    }: {
      isLastFrame: boolean
      frameBuffer: ArrayBuffer
    }) => {
      if (recordWS.readyState === recordWS.OPEN) {
        const params = {
          data: {
            status: isLastFrame ? 2 : 1,
            format: 'audio/L16;rate=16000',
            encoding: 'raw',
            audio: toBase64(frameBuffer)
          }
        }
        recordWS.send(JSON.stringify(params))
      }
    }
    recorder.onStop = () => {
      clearInterval(st)
      recordingDialogVisible.value = false
      if (Date.now() - recordInterval.value > 500) {
        recordDialog.value = true
      } else {
        ElMessage({
          type: 'error',
          message: '说话时间太短'
        })
      }
    }
  }

  // 检查是否有麦克风权限
  async function hasMicrophoneAccess() {
    const { start } = useUserMedia({ constraints: { audio: true } })
    try {
      await start()
      firstTimeGetAccess = false
      return true
    } catch {
      ElMessage({
        type: 'error',
        message: '请开启麦克风权限'
      })
      throw '请开启麦克风权限'
    }
  }

  // 首次加载只需要检查麦克风权限
  let firstTimeGetAccess = true

  // 开始录音
  async function startRecord() {
    if (firstTimeGetAccess) {
      hasMicrophoneAccess()
    } else {
      await hasMicrophoneAccess()
      recorder.start({
        sampleRate: 16000,
        frameSize: 1280
      })
    }
  }

  // 连接科大讯飞的语音识别接口
  // https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E8%B0%83%E7%94%A8%E7%A4%BA%E4%BE%8B
  function connectRecordWebSocket() {
    clearInterval(st)
    recordInterval.value = Date.now()
    if (recordWS) {
      recordWS.close()
    }
    const websocketUrl = getRecordWebSocketUrl()
    recordWS = new WebSocket(websocketUrl)
    recordStatus.value = 'CONNECTING'
    recordMessage.value = ''
    recordWS.onopen = () => {
      recordStatus.value = 'OPEN'
      const params = {
        common: {
          app_id: (window as any).APP_ID
        },
        business: {
          language: 'zh_cn',
          domain: 'iat',
          accent: languages[currentLanguage.value].accent,
          vad_eos: 60000,
          rlang: languages[currentLanguage.value].rlang
          // dwa: 'wpgs'
        },
        data: {
          status: 0,
          format: 'audio/L16;rate=16000',
          encoding: 'raw'
        }
      }
      recordWS.send(JSON.stringify(params))
      initCountdown()
    }
    recordWS.onmessage = (e) => {
      renderRecordResult(e.data)
    }
    recordWS.onclose = () => {
      recordStatus.value = 'CLOSED'
    }
  }

  // 初始化录音中的倒计时
  function initCountdown() {
    countdown.value = 60
    st = setInterval(() => {
      if (countdown.value === 0) {
        clearInterval(st)
        endRecord()
      } else {
        countdown.value -= 1
      }
    }, 1000)
  }

  // 生成语音识别接口
  // https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B
  // https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E8%B0%83%E7%94%A8%E7%A4%BA%E4%BE%8B
  function getRecordWebSocketUrl() {
    let url = 'wss://iat-api.xfyun.cn/v2/iat'
    const host = 'iat-api.xfyun.cn'
    const apiKey = (window as any).API_KEY
    const apiSecret = (window as any).API_SECRET
    const date = new Date().toUTCString()
    const algorithm = 'hmac-sha256'
    const headers = 'host date request-line'
    const signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`
    const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
    const signature = CryptoJS.enc.Base64.stringify(signatureSha)
    const authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
    const authorization = btoa(authorizationOrigin)
    url = `${url}?authorization=${authorization}&date=${date}&host=${host}`
    return url
  }

  // https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B
  function toBase64(buffer: ArrayBuffer) {
    let binary = ''
    const bytes = new Uint8Array(buffer)
    const len = bytes.byteLength
    for (let i = 0; i < len; i++) {
      binary += String.fromCharCode(bytes[i])
    }
    return window.btoa(binary)
  }

  // 解析语音识别接收的数据
  function renderRecordResult(resultData: string) {
    const jsonData = JSON.parse(resultData)
    if (jsonData.data && jsonData.data.result) {
      const data = jsonData.data.result
      let str = ''
      const ws = data.ws
      for (let i = 0; i < ws.length; i++) {
        str = str + ws[i].cw[0].w
      }
      recordMessage.value += str
    }
    if (jsonData.code === 0 && jsonData.data.status === 2) {
      recordWS.close()
    }
    if (jsonData.code !== 0) {
      recordWS.close()
      console.error(jsonData)
    }
  }

  // 关闭识别结果弹窗
  function closeRecordDialog() {
    recordWS.close()
    recordDialog.value = false
    recordMessage.value = ''
    recordingDialogVisible.value = false
  }

  // 结束录音
  async function endRecord() {
    if (!firstTimeGetAccess) {
      recorder.stop()
    }
  }

  return {
    initRecorder,
    connectRecordWebSocket,
    recordDialog,
    recordMessage,
    recordText,
    closeRecordDialog,
    recordingDialogVisible,
    endRecord,
    startRecord
  }
}
